package graph;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DatabaseStats;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;

public class IdManager {
	static class IdText {
		IdText(int id, String text) {
			m_id = id;
			m_text = text;
		}

		public int m_id;
		public String m_text;
	}

	static class IdManagerIterator {

		IdManagerIterator(IdManager manager) {
			m_db = manager.m_directDb;
			try {
				Cursor cursor = m_db.openCursor(null, null);
				m_cursor = cursor;
				m_nextIdText = getNext();
			} catch (DatabaseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		boolean hasNext() {
			if (m_nextIdText == null) {
				return false;
			} else {
				return true;
			}
		}

		IdText next() {
			IdText nextIdText = m_nextIdText;
			m_nextIdText = getNext();
			return nextIdText;
		}

		private IdText getNext() {
			DatabaseEntry key = new DatabaseEntry();
			DatabaseEntry data = new DatabaseEntry();
			OperationStatus status;
			try {
				status = m_cursor.getNext(key, data, LockMode.DEFAULT);
			} catch (DatabaseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				status = OperationStatus.NOTFOUND;
			}
			if (status == OperationStatus.NOTFOUND) {
				return null;
			} else {
				IdText nextIdText = new IdText(getId(key), getText(data));
				return nextIdText;
			}
		}

		Database m_db;
		Cursor m_cursor;
		IdText m_nextIdText;
	}

	public static IdManager getIdManager(String environmentName, String name) {
		String directName = name + "_direct.db";
		String inverseName = name + "_inverse.db";

		try {
			// Open the environment. Create it if it does not already exist.
			EnvironmentConfig envConfig = new EnvironmentConfig();
			// envConfig.setCacheSize(56 * 1024 * 1024);
			envConfig.setAllowCreate(true);
			Environment env = new Environment(new File(
					environmentName), envConfig);

			// Open the database. Create it if it does not already exist.
			DatabaseConfig dbConfig = new DatabaseConfig();
			dbConfig.setAllowCreate(true);
			Database direct_db = env.openDatabase(null, directName,
					dbConfig);
			Database inverse_db = env.openDatabase(null, inverseName,
					dbConfig);
			IdManager manager = new IdManager(env, direct_db, inverse_db);
			return manager;
		} catch (DatabaseException dbe) {
			// Exception handling goes here
		}

		return null;
	}

	private IdManager(Environment env, Database directDb, Database inverseDb) {
		m_env = env;
		m_directDb = directDb;
		m_inverseDb = inverseDb;
	}

	public IdManagerIterator getIterator() {
		return new IdManagerIterator(this);
	}

	public String getText(int id) {
		DatabaseEntry keyEntry = getIdEntry(id);
		DatabaseEntry valueEntry = new DatabaseEntry();
		try {
			m_directDb.get(null, keyEntry, valueEntry, LockMode.DEFAULT);
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return getText(valueEntry);
	}

	public int getId(String text) {
		DatabaseEntry keyEntry = getTextEntry(text);
		DatabaseEntry valueEntry = new DatabaseEntry();
		try {
			m_inverseDb.get(null, keyEntry, valueEntry, LockMode.DEFAULT);
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return getId(valueEntry);
	}

	static DatabaseEntry getIdEntry(int id) {
		ByteBuffer buffer = ByteBuffer.allocate(4);
		buffer.putInt(id);
		DatabaseEntry entry = new DatabaseEntry(buffer.array());
		return entry;
	}

	
	static DatabaseEntry getTextEntry(String text) {
		DatabaseEntry entry = new DatabaseEntry(text.getBytes());
		return entry;
	}

	static int getId(DatabaseEntry entry) {
		ByteBuffer buffer = ByteBuffer.wrap(entry.getData());
		return buffer.getInt();
	}

	static String getText(DatabaseEntry entry) {
		return new String(entry.getData());
	}

	public void load(File file) {
		try {
			BufferedReader reader = new BufferedReader(new FileReader(file));
			String line = reader.readLine();
			int maxId = 0;
			while (line != null) {
				int spacePos = line.lastIndexOf(" ");
				String name = line.substring(0, spacePos);
				int id = Integer.parseInt(line.substring(spacePos + 1));
				DatabaseEntry idEntry = getIdEntry(id);
				DatabaseEntry nameEntry = getTextEntry(name);
				if (id > maxId) {
					maxId = id;
				}
				try {
					OperationStatus status;
					status = m_directDb.put(null, idEntry, nameEntry);
//					 System.out.println(status.toString());
					status = m_inverseDb.put(null, nameEntry, idEntry);
//					 System.out.println(status.toString());
				} catch (DatabaseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				line = reader.readLine();
			}
			sync();

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}

	void release() {
		try {
			m_directDb.close();
			m_inverseDb.close();
			m_env.close();
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	Environment m_env;
	// id->text
	Database m_directDb;
	// text->id
	Database m_inverseDb;
	int m_nextId = 1;
	
	public void sync(){
		try {
			m_env.sync();
		} catch (DatabaseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		IdManager manager = IdManager.getIdManager("/home/czhai/jinghe/local/WORK/google-code/twitter-se/data/graph", "user");
		IdManagerIterator it = manager.getIterator();
		while(it.hasNext()){
			IdText idText = it.next();
			System.out.printf("%d %s\n", idText.m_id, idText.m_text);
		}
	}
}
